#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "shell.h"
#include "fs.h"
#include "disk.h"


/**
 * @brief get command from standard input
 * 
 * @param buf :buffer storing characters
 * @param buflen :length of buffer
 * @return int 0
 */
int getcmd(char *buf, int buflen)
{
    printf("SHELL@ ");
    memset(buf, 0, buflen);
    // fgets(buf, buflen, stdin);
    scanf("%[^\n]", buf);
    getchar();      // get enter in buffer

    buf[strlen(buf)] = '\n';

  	return 0;
}


/**
 * @brief parse the input buffer into argv array
 * 
 * @param buf :buffer storing characters
 * @param argv :arguments
 * @return int :count of arguments
 */
int getargs(char* buf, char* argv[])
{
	int argc = 0;
	int i = 0;
	while(buf[i] != '\n')
	{
		while(buf[i] == ' ' && buf[i] != '\n')
			i++;
		if(buf[i] == '\n')
		{
			break;
		}
		// get an argument
		argv[argc++] = buf + i;
		while(buf[i] != ' ' && buf[i] != '\n')
			i++;
		if(buf[i] == '\n')
		{
			buf[i] = '\0';
			break;
		}
		buf[i++] = '\0';
	}
	// end of argv must be 0
	argv[argc] = 0;
	// return the count of argv
	return argc;
}


int exec_touch(char* argv[], int argc)
{
    char *p;
    uint32_t inode_num;

    if(argc != 2)
    {
        printf("Must have two arguments!\n");
        return 0;
    }

    p = argv[1] + strlen(argv[1]) - 1;
    if(*p == '/')
    {
        printf("Can't create a directory.\n");
        printf("Do you mean mkdir\n");
        return 0;
    }
    while(p >= argv[1] && *p != '/')
        p--;
    if(p >= argv[1])
    {
        *p = 0;
        inode_num = open_path(argv[1]);
    }
    else
    {
        inode_num = open_path("/");
    }
    p++;

    if(inode_num <= 0)
    {
        printf("Create file failed!\n");
        return 0;
    }
    
    if(create_fd(inode_num, p, T_FILE) <= 0)
        printf("Create file failed!\n");

    return 0;
}


int exec_mkdir(char* argv[], int argc)
{
    char *p;
    uint32_t inode_num;

    if(argc != 2)
    {
        printf("Must have two arguments!\n");
        return 0;
    }

    p = argv[1] + strlen(argv[1]) - 1;
    if(*p == '/')
        *p = 0;
    while(p >= argv[1] && *p != '/')
        p--;
    if(p >= argv[1])
    {
        *p = 0;
        inode_num = open_path(argv[1]);
    }
    else
    {
        inode_num = open_path("/");
    }
    p++;

    if(inode_num <= 0)
    {
        printf("Create directory failed!\n");
        return 0;
    }

    if(create_fd(inode_num, p, T_DIRECTORY) <= 0)
        printf("Create directory failed!\n");

    return 0;
}


int exec_cp(char* argv[], int argc)
{
    uint32_t src_inum;
    uint32_t dst_pinum;
    char *dst_name;

    if(argc != 3)
    {
        printf("Must have three arguments!\n");
        return 0;
    }

    // printf("inode=%d\n", open_path(argv[1]));

    dst_name = argv[2] + strlen(argv[2]) - 1;
    if(*dst_name == '/')   // use the same file name as the source file in argv[1]
    {
        dst_name = argv[1] + strlen(argv[1]) - 1;
        while(dst_name >= argv[1] && *dst_name != '/')
            dst_name--;
        dst_name++;
        dst_pinum = open_path(argv[2]);
    }
    else    // use another file name in argv[2]
    {
        while(dst_name >= argv[2] && *dst_name != '/')
            dst_name--;
        if(*dst_name == '/')
        {
            *dst_name = 0;      // argv[2]: parent_path + file_name
            dst_pinum = open_path(argv[2]);
        }
        else
        {
            dst_pinum = open_path("/");
        }      
        dst_name++;
    }
    src_inum = open_path(argv[1]);
    
    if(src_inum < 1)
    {
        printf("cannot stat \'%s\': No such file\n", argv[1]);
        return 0;
    }
    if(dst_pinum <= 0)
    {
        printf("cannot stat \'%s\': No such directory\n", argv[2]);
        return 0;
    }

    if(copy_file(src_inum, dst_pinum, dst_name) <= 0)
    {
        printf("copy file failed\n");
        return 0;
    }

    return 0;
}


int exec_ls(char* argv[], int argc)
{
    int cnt, i;
    uint32_t inode_num;

    if(argc > 2)
    {
        printf("At most two arguments!\n");
        return 0;
    }

    if(argc == 1)
        inode_num = open_path("/");     // open root directory
    else if(argc == 2)
    {
        // end with '/' to denote it is a directory
        if(argv[1][strlen(argv[1]) - 1] != '/')
            strcat(argv[1], "/");
        inode_num = open_path(argv[1]);
    }

    if(inode_num <= 0)
    {
        printf("cannot open path \'%s\'\n", argv[1]);
        return 0;
    }

    list_dir(inode_num);

    return 0;
}


int exec_shutdown()
{
    printf("closing file system...\n");
    close_disk();
    printf("file system closed.\n\n");
    return -1;
}


int runcmd(char* argv[], int argc)
{
    if(argc==0)
        return 0;
    else if(strcmp(argv[0], "shutdown") == 0)
        return exec_shutdown();
    else if(strcmp(argv[0], "touch") == 0)
        return exec_touch(argv, argc);
    else if(strcmp(argv[0], "mkdir") == 0)
        return exec_mkdir(argv, argc);
    else if(strcmp(argv[0], "cp") == 0)
        return exec_cp(argv, argc);
    else if(strcmp(argv[0], "ls") == 0)
        return exec_ls(argv, argc);
    else 
    {
        printf("%s :command not found\n", argv[0]);
        return 0;
    }
}


void simple_shell()
{
    char buf[MAXBUF];
    char* argv[MAXARGS];
    int argc;

    // Read and run input commands.
  	while(getcmd(buf, sizeof(buf)) >= 0)
  	{
		argc = getargs(buf, argv);
		if(runcmd(argv, argc) < 0)
            break;
  	}
}